package com.ProcessManagement;

import com.Memory.MemoryManager;
import com.hardware.Clock;
import com.ui.ProcessSchedulerUI;

import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;

public class Scheduler {
    private final int MEMORY_SIZE = 128; // 内存总大小（单位：K）
    private final int OS_SIZE = 4; // 操作系统占用空间大小（单位：K）
    private final int AVAILABLE_MEMORY_START = OS_SIZE; // 可分配空间的起始地址
    public List<List<Integer>> sequences;
    private int id = 1;
    private List<PCB> pcbList1;
    private List<Partition> allocatedPartitions = new ArrayList<>(); // 已分配分区列表
    private List<Partition> unallocatedPartitions = new ArrayList<>(); // 未分配分区列表

    private List<PCB> pcbList;
    private boolean isrun = false;
    private int nowPCB;
    boolean flag1 = true;
    public Thread thread;

    public Scheduler(List<PCB> pcbList) {
        this.pcbList = pcbList;
    }

    public Scheduler() {
        initializeMemory();
        sequences = readAccessingVariables("src/input/AccessingVariables.txt");
    }

    public void setPCBList(List<PCB> pcbList){
        this.pcbList = pcbList;
    }

    public void start(SchedulingAlgorithm algorithm) {
        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(100);
                    if (pcbList!=null && !pcbList.isEmpty()) {
                        switch (algorithm) {
                            case FCFS:
                                fcfs();
                                break;
                            case SJF:
                                sjf();
                                break;
                            case PRIORITY:
                                priorityScheduling();
                                break;
                            case ROUND_ROBIN:
                                roundRobin();
                                break;
                            default:
                                System.out.println("无效的调度算法!");
                        }
                    }
                } catch (InterruptedException e){
                    ProcessSchedulerUI.updateProcessOutput("阻塞进程: " + nowPCB + "!!!");
                    e.printStackTrace();
                    break;
                }

            }

        });
        thread.start();
    }

    public synchronized void suspend() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e){
            Thread.currentThread().interrupt();
        }
    }

    public void resume() {
//        notify();
        isrun = true;
    }

    private void fcfs() {
        // 先来先服务调度算法的实现
        // 根据进程的创建时间进行排序，然后依次执行
        pcbList.sort((pcb1, pcb2) -> pcb1.getStartTimes() - pcb2.getStartTimes());

        // 执行调度
        Iterator<PCB> iterator = pcbList.iterator();
        while (iterator.hasNext()){
            PCB pcb = iterator.next();
            nowPCB = pcb.getProID();
            executeProcess(pcb);
            for (PCB pcb1 : PCBReader.getInstance().getPCBList()) {
                if (pcb1.getProID() == pcb.getProID()){
                    pcb1.setEndTimes(Clock.getInstance().getCurrentTime());
                    pcb1.setTotalTimes();
                }
            }
            //                    打印pcb列表
            ProcessSchedulerUI.updatePCBList("更新PCB列表");
            PCBReader.getInstance().printfPCBInfo();
            iterator.remove();
        }
    }

    private void sjf() {
        // 短作业优先调度算法的实现
        // 根据进程包含的指令数进行排序，指令数越少的进程优先执行
        pcbList.sort((pcb1, pcb2) -> pcb1.getInstructNum() - pcb2.getInstructNum());

        // 执行调度
        Iterator<PCB> iterator = pcbList.iterator();
        boolean flagarray[] = {true,true,true};
        while (iterator.hasNext()){
            PCB pcb = iterator.next();
            nowPCB = pcb.getProID();
            boolean flag = true;
            for (Partition partition : allocatedPartitions) {
                if (partition.getName() == pcb.getProID()){
                    flag = false;
                }
            }
            for (Partition partition : unallocatedPartitions) {
                if (partition.getName() == pcb.getProID()){
                    flag = false;
                }
            }
            if (flag &&flagarray[pcb.getProID()-1]){
                if (pcb.getProID() ==1 ){
                    if (flag1) {
                        allocateVariablePartition(pcb.getProID(),pcb.getInstructNum());
                        LRU(pcb.getProID());
                    }
                    flag1 = false;
                }
                else {
                    allocateVariablePartition(pcb.getProID(),pcb.getInstructNum());
                    LRU(pcb.getProID());
                }
                flagarray[pcb.getProID()-1] = false;
            }
            executeProcess(pcb);
            for (PCB pcb1 : PCBReader.getInstance().getPCBList()) {
                if (pcb1.getProID() == pcb.getProID()){
                    pcb1.setEndTimes(Clock.getInstance().getCurrentTime());
                    pcb1.setTotalTimes();
                    deallocateVariablePartition(pcb1.getProID());
                }
            }
            //                    打印pcb列表
            ProcessSchedulerUI.updatePCBList("更新PCB列表");
            PCBReader.getInstance().printfPCBInfo();
            iterator.remove();
        }
        ProcessSchedulerUI.updateMemoryAllocation("整合空闲内存！！！");
        displayMemoryStatus();
    }

    private void priorityScheduling() {
        // 优先级调度算法的实现
        // 根据进程的优先级进行排序，优先级越高的进程优先执行
        pcbList.sort((pcb1, pcb2) -> pcb2.getPriority() - pcb1.getPriority());

        // 执行调度
        Iterator<PCB> iterator = pcbList.iterator();
        while (iterator.hasNext()){
            PCB pcb = iterator.next();
            nowPCB = pcb.getProID();
            executeProcess(pcb);
            for (PCB pcb1 : PCBReader.getInstance().getPCBList()) {
                if (pcb1.getProID() == pcb.getProID()){
                    pcb1.setEndTimes(Clock.getInstance().getCurrentTime());
                    pcb1.setTotalTimes();
                }
            }
            //                    打印pcb列表
            ProcessSchedulerUI.updatePCBList("更新PCB列表");
            PCBReader.getInstance().printfPCBInfo();
            iterator.remove();
        }
    }

    private void roundRobin() {
        // 轮转调度算法的实现
        // 假设每个进程的时间片为 2
        int timeSlice = 2;
        boolean flagarray[] = {true,true,true};
        pcbList.sort((pcb1, pcb2) -> pcb1.getStartTimes() - pcb2.getStartTimes());
        // 执行调度
        while (!pcbList.isEmpty()) {
            PCB currentProcess = pcbList.get(0);
            nowPCB = currentProcess.getProID();
//            allocateVariablePartition(currentProcess.getProID(),currentProcess.getInstructNum());
            executeProcess(currentProcess, timeSlice);
//            防止重复分配
            if (flagarray[currentProcess.getProID()-1]){
                if (currentProcess.getProID() ==1 ){
                    if (flag1){
                        allocateVariablePartition(currentProcess.getProID(),currentProcess.getInstructNum());
                        LRU(currentProcess.getProID());
                    }
                    flag1 = false;
                }
                else {
                    allocateVariablePartition(currentProcess.getProID(),currentProcess.getInstructNum());
                    LRU(currentProcess.getProID());
                }
                flagarray[currentProcess.getProID()-1] = false;
            }
            if (currentProcess.getTurnTimes() > 0) {
                // 将当前进程移到列表末尾，以便下一次轮转
                pcbList.remove(0);
                pcbList.add(currentProcess);
            } else {
                for (PCB pcb1 : PCBReader.getInstance().pcbList) {
                    if (pcb1.getProID() == pcbList.get(0).getProID()){
                        pcb1.setEndTimes(Clock.getInstance().getCurrentTime());
                        pcb1.setTotalTimes();
                        deallocateVariablePartition(pcb1.getProID());
                    }
                }
                //                    打印pcb列表
                ProcessSchedulerUI.updatePCBList("更新PCB列表");
                PCBReader.getInstance().printfPCBInfo();
                pcbList.remove(0);
            }
        }
        ProcessSchedulerUI.updateMemoryAllocation("------ 整合空闲内存 ------");
        unallocatedPartitions.clear();
        initializeMemory();
        displayMemoryStatus();
    }

//    传值为pcb
    private void executeProcess(PCB pcb) {
        pcb.setPSW("Ready");
        // 具体的操作逻辑...
        Iterator<Instruction> iterator = pcb.getPcbInfoList().iterator();
        while (iterator.hasNext()){
            Instruction instruction = iterator.next();
            try {
                if (instruction.getInstructionType() == 0) Thread.sleep(1000);
                else if (instruction.getInstructionType() == 1) Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            iterator.remove();
            pcb.setTurnTimes();
            ProcessSchedulerUI.updateProcessOutput("进程ID: " + pcb.getProID()+" 执行指令："+instruction.getInstructionNumber()+" 指令类型："+instruction.getInstructionType()+" 仍需时间: " + pcb.getTurnTimes());
        }

        // 其他统计信息更新...
        // 其他统计信息输出...
    }

//    传值为pcb和时间片
    private void executeProcess(PCB pcb, int timeSlice){
//        剩余时间片
        int remainTime = timeSlice;
        pcb.setPSW("Ready");
        // 具体的操作逻辑...
        Iterator<Instruction> iterator = pcb.getPcbInfoList().iterator();
        while (iterator.hasNext()){
            Instruction instruction = iterator.next();
            if (instruction.getInstructionTime() <= remainTime){
                try {
                    if (instruction.getInstructionType() == 0) Thread.sleep(1000);
                    else if (instruction.getInstructionType() == 1) Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                iterator.remove();
//              更新剩余时间片
                remainTime = remainTime - instruction.getInstructionTime();
                pcb.setTurnTimes();
                ProcessSchedulerUI.updateProcessOutput("进程ID: " + pcb.getProID()+" 执行指令："+instruction.getInstructionNumber()+" 指令类型："+instruction.getInstructionType()+" 仍需时间: " + pcb.getTurnTimes());
            }
        }

        // 其他统计信息更新...
        // 其他统计信息输出...
    }

    //    最近最少使用调度算法（LRU）,内存管理
    public void LRU(int id) {
//        读取pcb_list.txt文件并获取PCB列表。
        pcbList1 = readPCBFromFile("src/input/pcb_list.txt");
        //        Memory[100] 有124个物理块#0表示没有分配 1表示分配 初始化时0-39为1,其他为0
        int[] Memory = new int[124];
        for (int i = 0; i < 124; i++) {
            Memory[i]=0;
        }
        for (int i = 0; i < 40; i++) {
            Memory[i]=1;
        }
        for (PCB pcb : pcbList1) {
            if (id == pcb.getProID()){
                //        Pagelist #页号，内存块号
                int[] Pagelist;
                Pagelist = request(pcb.getInstructNum()*10,40);
//        从Memory中找到4个空白页，分配给进程，更新pagelist
                for (int j = 0; j <4; j++) {
                    for (int i = 0; i < 100; i++) {
                        if (Memory[i]==0){
                            Pagelist[j]=i;
                            Memory[i]=1;
                            break;
                        }
                    }
                }
//        Stack[4]  #记录访问
                int[] Stack = new int[4];
//        初始化为-1
                for (int i = 0; i < 4; i++) {
                    Stack[i]=-1;
                }

                ProcessSchedulerUI.updateAccessingVariables("-----------------");
                ProcessSchedulerUI.updateAccessingVariables("进程ID："+pcb.getProID());

//        定义访问变量，调用read（），并记录缺页次数，返回缺页次数
                int[] array= getRowValues(sequences, pcbList1.indexOf(pcb));
//        缺页数
                int count=0;
                for (int i = 0; i < array.length; i++) {
                    ProcessSchedulerUI.updateAccessingVariables("访问页表："+array[i]);
                    if (!read(array[i],Stack,Pagelist,Memory)){
                        count++;
                    }
                    ProcessSchedulerUI.updateAccessingVariables("Stack记录：");
                    String result = "";
                    for (int j = 0; j < Stack.length; j++) {
                        result += Stack[j];
                        if (j < Stack.length - 1) {
                            result += ", ";
                        }
                    }
                    ProcessSchedulerUI.updateAccessingVariables(result);
                    //        页表Pagelist的占用情况
                    ProcessSchedulerUI.updateAccessingVariables("页表Pagelist分配的物理块号：");
                    String result2 = "";
                    for (int j = 0; j < Pagelist.length; j++) {
                        result2 += Pagelist[j];
                        if (j < Pagelist.length - 1) {
                            result2 += ", ";
                        }
                    }
                    ProcessSchedulerUI.updateAccessingVariables(result2);
                    ProcessSchedulerUI.updateAccessingVariables("");
                }
                ProcessSchedulerUI.updateAccessingVariables("缺页数为："+count);
//            回收内存
                for (int i = 40; i < Memory.length; i++) {
                    Memory[i]=0;
                }
            }
        }

    }

    //        实现访问页面的模拟操作，返回false，表示发生缺页，调用后返回页面，返回true，表示未发生缺页，直接返回。
    public boolean read(int page_number,int[] Stack,int[] Pagelist,int[] Memory){
        for (int i = 0; i < Stack.length; i++) {
            if (page_number==Stack[i]){
                for (int j =i; j >0; j--) {
                    Stack[j]=Stack[j-1];
                }
                Stack[0]=page_number;
                return true;
            }
        }
//        设置Pagelist
        if (Pagelist[page_number]==-1){
            for (int i = 0; i < Memory.length; i++) {
                if (Memory[i]==0){
                    Pagelist[page_number]=i;
                    Memory[i]=1;
                    break;
                }
            }
        }
        if (Stack[Stack.length-1]!=-1) Pagelist[Stack[Stack.length-1]]=-1;
        for (int i = Stack.length-1; i >0; i--) {
            Stack[i]=Stack[i-1];
        }
        Stack[0]=page_number;
        return false;
    }

    //    读取AccessingVariables文件
    public List<List<Integer>> readAccessingVariables (String fileName) {
        List<List<Integer>> sequences = new ArrayList<>();

        try {
            BufferedReader reader = new BufferedReader(new FileReader(fileName));
            String line;
            while ((line = reader.readLine()) != null) {
                List<Integer> sequence = parseLine(line);
                sequences.add(sequence);
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return sequences;
    }

    //    读取AccessingVariables文件后数据处理
    private List<Integer> parseLine(String line) {
        String[] tokens = line.split(",");
        List<Integer> sequence = new ArrayList<>();
        for (String token : tokens) {
            sequence.add(Integer.parseInt(token));
        }
        return sequence;
    }

    //    从List<List<Integer>>提起一行数据
    public int[] getRowValues(List<List<Integer>> sequences, int rowIndex) {
        List<Integer> row = sequences.get(rowIndex);
        int[] rowValues = new int[row.size()];

        for (int i = 0; i < row.size(); i++) {
            rowValues[i] = row.get(i);
        }

        return rowValues;
    }

    //        Pagelist长度为⌈total/10⌉，返回pagelist
    public int[] request(int total,int getMax){
        int count = (int)total/10;
        int[] Pagelist=new int[count];
//        初始化为-1
        for (int i = 0; i < Pagelist.length; i++) {
            Pagelist[i]=-1;
        }
        return Pagelist;
    }

    //    读pcb
    public List<PCB> readPCBFromFile(String fileName) {
        List<PCB> pcbList = new ArrayList<>();

        File file = new File(fileName);
        try {
            Scanner scanner = new Scanner(file);
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();
                String[] parts = line.split(" ");

                if (parts.length == 4) {
                    int proID = Integer.parseInt(parts[0]);
                    int startTimes = Integer.parseInt(parts[1]);
                    int instructNum = Integer.parseInt(parts[2]);
                    int Priority = Integer.parseInt(parts[3]);
                    // 创建PCB对象并添加到列表中
                    PCB pcb = new PCB(proID, startTimes, instructNum, Priority);
                    pcb.setPSW("null");  // 设置进程状态为null
                    pcb.setEndTimes(0);  // 设置结束时间
                    pcbList.add(pcb);
                }
                else if(parts.length == 3){
                    int proID = Integer.parseInt(parts[0]);
                    int startTimes = Integer.parseInt(parts[1]);
                    int instructNum = Integer.parseInt(parts[2]);
                    int Priority = 0;
                    // 创建PCB对象并添加到列表中
                    PCB pcb = new PCB(proID, startTimes, instructNum, Priority);
                    pcb.setPSW("null");  // 设置进程状态为null
                    pcb.setEndTimes(0);  // 设置结束时间
                    pcbList.add(pcb);
                }
                else System.out.println("文件job_list.txt格式错误!!!");
            }
            scanner.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        return pcbList;
    }

//    可分配内存块
//    可变分区分配空间
    private void allocateVariablePartition(int id, int jobSize) {
        this.id = id;

    // 在未分配分区中查找合适的分区
    Partition selectedPartition = findVariablePartition(jobSize);

    if (selectedPartition != null) {
        // 更新分区信息
        selectedPartition.setAllocated(true);
        selectedPartition.setJobSize(jobSize);

        // 将已分配分区添加到已分配分区列表
        allocatedPartitions.add(selectedPartition);

        // 输出内存状况
        ProcessSchedulerUI.updateMemoryAllocation("分配成功！");
        displayMemoryStatus();
    } else {
        ProcessSchedulerUI.updateMemoryAllocation("没有足够的空间来分配作业。");
    }
}

//    可变分区根据进程的大小动态的建立分区
    private Partition findVariablePartition(int jobSize) {
        // 在未分配分区中查找合适的分区
        for (Partition partition : unallocatedPartitions) {
            if (partition.getSize() > jobSize) {
                // 分割出合适的分区
                int newStartAddress = partition.getStartAddress() + jobSize;
                int newSize = partition.getSize() - jobSize;

                // 创建新分区
                Partition newPartition = new Partition(partition.getStartAddress(), jobSize, false, 0,id);

                // 更新原分区信息
                partition.setSize(newSize);
                partition.setJobSize(0);
                partition.setStartAddress(newStartAddress);

                return newPartition;
            } else if (partition.getSize() == jobSize) {
                // 分区大小正好等于作业大小
                return partition;
            }
        }
        return null;
    }

//    可变分区的释放
    private void deallocateVariablePartition(int partitionName){
        // 在已分配分区中查找要释放的分区
        int partitionID = -1;
        for (Partition partition : allocatedPartitions) {
            if (partition.getName()==partitionName) {
                partitionID = allocatedPartitions.indexOf(partition);
            }
        }

        if (partitionID != -1) {
            // 更新分区信息
            allocatedPartitions.get(partitionID).setAllocated(false);
            allocatedPartitions.get(partitionID).setJobSize(0);

            // 添加释放的分区到未分配分区列表
            unallocatedPartitions.add(allocatedPartitions.get(partitionID));

            // 将已分配分区从已分配分区列表中移除
            allocatedPartitions.remove(partitionID);

            // 合并相邻的未分配分区
            mergeAdjacentPartitions();

            // 输出内存状况
            ProcessSchedulerUI.updateMemoryAllocation("释放成功！");
            displayMemoryStatus();
        } else {
            ProcessSchedulerUI.updateMemoryAllocation("未找到要释放的分区。");
        }
    }

//    合并可变分区的相邻的未分配分区
    private void mergeAdjacentPartitions() {
        // 遍历未分配分区列表，合并相邻的未分配分区
        for (int i = 0; i < unallocatedPartitions.size() - 1 && i>=0; i++) {
            Partition currentPartition = unallocatedPartitions.get(i);
            Partition nextPartition = unallocatedPartitions.get(i + 1);

            // 如果当前分区和下一个分区是相邻的，则合并它们
            if (nextPartition.getStartAddress() + nextPartition.getSize() == currentPartition.getStartAddress()) {
                // 更新当前分区的大小
                currentPartition.setSize(currentPartition.getSize() + nextPartition.getSize());
                currentPartition.setStartAddress(nextPartition.getStartAddress());

                // 从未分配分区列表中移除下一个分区
                unallocatedPartitions.remove(i + 1);

                // 因为移除了一个分区，需要减小索引以继续遍历
                i--;
            }
        }
    }

    private void displayMemoryStatus() {
        ProcessSchedulerUI.updateMemoryAllocation("------ 内存分配情况 ------");
        ProcessSchedulerUI.updateMemoryAllocation("已分配分区：");
        for (Partition partition : allocatedPartitions) {
            ProcessSchedulerUI.updateMemoryAllocation("分区名字："+partition.getName()+" 起始地址："+partition.getStartAddress()+"k 空间大小："+partition.getSize()+"k 作业大小："+partition.jobSize+"k");
        }

        ProcessSchedulerUI.updateMemoryAllocation("未分配分区：");
        for (Partition partition : unallocatedPartitions) {
            ProcessSchedulerUI.updateMemoryAllocation("分区名字："+partition.getName()+" 起始地址："+partition.getStartAddress()+"k 空间大小："+partition.getSize()+"k 作业大小："+partition.jobSize+"k");
        }
    }

    //    初始化可变分区的未分配分区
    private void initializeMemory() {
        // 创建初始的未分配分区
        Partition initialPartition = new Partition(OS_SIZE, MEMORY_SIZE - OS_SIZE, false, 0, 0);
        unallocatedPartitions.add(initialPartition);
    }

//    分区单元类
    private class Partition {
        private int startAddress; // 起始地址
        private int size; // 分区大小
        private boolean isAllocated; // 是否已分配
        private int jobSize; // 作业大小
        private int name; // 分区名称

        public Partition(int startAddress, int size, boolean isAllocated, int jobSize, int name) {
            this.startAddress = startAddress;
            this.size = size;
            this.isAllocated = isAllocated;
            this.jobSize = jobSize;
            this.name = name;
        }

        public int getStartAddress() {
            return startAddress;
        }

        public void setStartAddress(int StartAddress) {
            this.startAddress = StartAddress;
        }

        public int getSize() {
            return size;
        }

        public void setSize(int size) {
            this.size = size;
        }

        public boolean isAllocated() {
            return isAllocated;
        }

        public void setAllocated(boolean allocated) {
            isAllocated = allocated;
        }

        public int getJobSize() {
            return jobSize;
        }

        public void setJobSize(int jobSize) {
            this.jobSize = jobSize;
        }

        public int getName() {
            return name;
        }
    }
}
